package com.mytijian.openapi.adapter.dianping.service.impl;

import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.mytijian.openapi.adapter.dianping.client.DzopenUtils;
import com.mytijian.openapi.adapter.dianping.consts.AsyncRequestConsts;
import com.mytijian.openapi.adapter.dianping.consts.DzopenConsts;
import com.mytijian.openapi.adapter.dianping.exceptions.DzopenErrorInfo;
import com.mytijian.openapi.adapter.dianping.exceptions.DzopenException;
import com.mytijian.openapi.adapter.dianping.inner.param.DzAsyncTimemodifynotifyComfirmParam;
import com.mytijian.openapi.adapter.dianping.model.AsyncRequest;
import com.mytijian.openapi.adapter.dianping.model.DzopenOrderRel;
import com.mytijian.openapi.adapter.dianping.param.AsyncRequestQuery;
import com.mytijian.openapi.adapter.dianping.param.DzModifybooktimeParam;
import com.mytijian.openapi.adapter.dianping.param.DzopenOrderRelQuery;
import com.mytijian.openapi.adapter.dianping.result.DzModifybooktimeResult;
import com.mytijian.openapi.adapter.dianping.result.DzResult;
import com.mytijian.openapi.adapter.dianping.service.AsyncRequestService;
import com.mytijian.openapi.adapter.dianping.service.DzopenModifybooktimeService;
import com.mytijian.openapi.adapter.dianping.service.DzopenOrderRelService;
import com.mytijian.openapi.core.client.IOpenAPIClient;
import com.mytijian.openapi.core.client.param.ChangeExamDateParam;
import com.mytijian.openapi.core.client.result.BaseResult;
import com.mytijian.openapi.core.model.AppConfig;
import com.mytijian.openapi.core.service.OpenApiAppConfigService;
import com.mytijian.utils.DateUtils;
import com.mytijian.utils.Lists;

@Service("dzopenModifybooktimeService")
public class DzopenModifybooktimeServiceImpl implements DzopenModifybooktimeService {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private AsyncRequestService asyncRequestService;

	@Autowired
	private DzopenOrderRelService dzopenOrderRelService;

	@Autowired
	private OpenApiAppConfigService openApiAppConfigService;

	@Autowired
	private DzopenShopAuthServiceImpl dzopenShopAuthServiceImpl;

	@Autowired
	private IOpenAPIClient client;

	@Value("${dzopen.signkey}")
	private String signkey;
	
	@Value("${dzopen.asyncmaxtrycount}")
	private Integer maxTryCount;

	@Override
	public DzModifybooktimeResult modifybooktime(DzModifybooktimeParam param) {

		String requestParams = JSON.toJSONString(param);

		AsyncRequest async = new AsyncRequest();
		async.setRequestApp(AsyncRequestConsts.Apps.Dzopen);
		async.setRequestStatus(AsyncRequestConsts.RequestStatus.Unexecuted);
		async.setRequestType(AsyncRequestConsts.DzopenApp.modifydate);
		async.setRequestParam(requestParams);
		async.setRequestTryCount(0);
		async.setAsyncNotifyStatus(AsyncRequestConsts.NotifyStatus.Unsuccess);
		async.setAsyncNotifyTryCount(0);
		asyncRequestService.saveRequest(async);

		DzModifybooktimeResult result = new DzModifybooktimeResult();
		result.setOrder_id(param.getOrderId());

		return result;
	}

	@Override
	public void asyncModifybooktime() {
		AsyncRequestQuery query = new AsyncRequestQuery();
		query.setRequestApp(AsyncRequestConsts.Apps.Dzopen);
		query.setRequestStatus(AsyncRequestConsts.RequestStatus.Unexecuted);
		query.setRequestType(AsyncRequestConsts.DzopenApp.modifydate);
		query.setRequestTryCountLessThen(maxTryCount);

		List<AsyncRequest> requests = asyncRequestService.listRequests(query);

		if (Lists.isEmpty(requests)) {
			logger.info("无异步改期请求");
			return;
		}
		logger.info("需要处理 {} 条异步改期请求。", requests.size());
		requests.forEach(this::doModifybooktime);
	}

	@Override
	public void asyncNotify() {
		AsyncRequestQuery query = new AsyncRequestQuery();
		query.setRequestApp(AsyncRequestConsts.Apps.Dzopen);
		query.setRequestStatus(AsyncRequestConsts.RequestStatus.Executed);
		query.setRequestType(AsyncRequestConsts.DzopenApp.modifydate);
		query.setRequestTryCountLessThen(maxTryCount);
		query.setAsyncNotifyStatus(AsyncRequestConsts.NotifyStatus.Unsuccess);
		query.setAsyncNotifyTryCountLessThen(maxTryCount);
		List<AsyncRequest> requests = asyncRequestService.listRequests(query);

		if (Lists.isEmpty(requests)) {
			logger.info("无异步改期通知请求");
			return;
		}

		logger.info("需要处理 {} 条异步改期通知请求。", requests.size());

		for (AsyncRequest request : requests) {
			DzModifybooktimeParam dzParam = JSON.parseObject(request.getRequestParam(), DzModifybooktimeParam.class);

			this.doNotify(dzParam, request);
			
			this.asyncRequestService.updateRequest(request);
		}
	}

	public void doModifybooktime(AsyncRequest request) {

		DzModifybooktimeParam dzParam = JSON.parseObject(request.getRequestParam(), DzModifybooktimeParam.class);

		DzopenOrderRel orderRel = getOrderRel(dzParam);

		request.setRequestTryCount(request.getRequestTryCount() + 1);
		String modifyResult = "1";
		try {
			verifyOrderStatus(orderRel);

			verifyChangeExamDateResult(invokeChangeExamDate(dzParam, orderRel));
			
			orderRel.setExamDate(DateUtils.parse(dzParam.getBeginTime().substring(0, 11)));
			dzopenOrderRelService.updateDzopenOrderRel(orderRel);
			request.setRequestStatus(AsyncRequestConsts.RequestStatus.Executed);
		} catch (DzopenException e) {
			logger.error("[美团点评][订单改期] 接口执行失败", e);
			modifyResult = "2";
		}

		request.setRequestResult(modifyResult);

		doNotify(dzParam, request);

		asyncRequestService.updateRequest(request);
	}

	private void addNotifyTryCount(AsyncRequest request) {
		request.setAsyncNotifyTryCount(request.getAsyncNotifyTryCount() + 1);
		asyncRequestService.updateRequest(request);
	}

	private void doNotify(DzModifybooktimeParam dzParam, AsyncRequest request) {
		addNotifyTryCount(request);
		DzAsyncTimemodifynotifyComfirmParam dzAsyncmodifyTimeConfirm = new DzAsyncTimemodifynotifyComfirmParam();
		dzAsyncmodifyTimeConfirm.setAppKey(dzParam.getAppKey());
		dzAsyncmodifyTimeConfirm.setTimestamp(DateUtils.format(DateUtils.YYYY_MM_DD_HMS, new Date()));
		dzAsyncmodifyTimeConfirm.setV("1");
		dzAsyncmodifyTimeConfirm.setCode(String.valueOf(DzopenConsts.SuccessCode));
		dzAsyncmodifyTimeConfirm.setSignMethod("MD5");
		dzAsyncmodifyTimeConfirm.setFormat("json");

		dzAsyncmodifyTimeConfirm.setOrderId(dzParam.getOrderId());
		DzopenOrderRelQuery query = new DzopenOrderRelQuery();
		query.setDzOrderId(dzParam.getOrderId());
		DzopenOrderRel rel = dzopenOrderRelService.getOrderRel(query);

		dzAsyncmodifyTimeConfirm.setAppOrderId(rel.getOrderNum());
		dzAsyncmodifyTimeConfirm.setAppPeriodId(dzParam.getAppPeriodId());
		dzAsyncmodifyTimeConfirm.setBegintime(dzParam.getBeginTime());
		dzAsyncmodifyTimeConfirm.setCode(String.valueOf(DzopenConsts.SuccessCode));
		dzAsyncmodifyTimeConfirm.setModifyResult(request.getRequestResult());
		dzAsyncmodifyTimeConfirm.setAppShopId(dzParam.getAppShopId());
		dzAsyncmodifyTimeConfirm.setSession(dzopenShopAuthServiceImpl.getSession(dzParam.getAppShopId()));

		try {
			DzResult<?> notifyResult = DzopenUtils.sendRequest(DzopenConsts.Apis.Modifyresultcallback, this.signkey,dzAsyncmodifyTimeConfirm);
			if (DzopenConsts.SuccessCode.equals(notifyResult.getCode())) {
				logger.info("[美团点评][订单改期] 改期成功后通知美团成功 第{}次调用",request.getAsyncNotifyTryCount());
				request.setAsyncNotifyStatus(AsyncRequestConsts.NotifyStatus.Successed);
			}
		} catch (Exception e) {
			logger.error("[美团点评][订单改期] 改期成功后第 {} 次通知美团时发生异常 {}", e, request.getAsyncNotifyTryCount());
		}

	}

	private DzopenOrderRel getOrderRel(DzModifybooktimeParam dzParam) {
		DzopenOrderRelQuery query = new DzopenOrderRelQuery();
		query.setDzOrderId(dzParam.getOrderId());
		DzopenOrderRel orderRel = dzopenOrderRelService.getOrderRel(query);
		return orderRel;
	}

	private BaseResult invokeChangeExamDate(DzModifybooktimeParam dzParam, DzopenOrderRel orderRel) {
		ChangeExamDateParam c = new ChangeExamDateParam();
		c.setExamTimeIntervalId(Integer.valueOf(dzParam.getAppPeriodId()));
		c.setNewExamDate(dzParam.getBeginTime().substring(0, 11));
		c.setOrderNum(orderRel.getOrderNum());

		AppConfig config = openApiAppConfigService.getConfig(DzopenConsts.AppName);
		BaseResult result = client.changeExamDate(config, c);
		return result;
	}

	private void verifyChangeExamDateResult(BaseResult modifyResult) {
		if (modifyResult == null || !"T".equals(modifyResult.getSuccess())) {
			logger.error("开放平台返回值: {}", ToStringBuilder.reflectionToString(modifyResult));
			if ("201017".equals(modifyResult.getErrCode())) {
				throw new DzopenException(DzopenErrorInfo.ORDER_MODIFY_CONSUMED);
			}
			throw new DzopenException(DzopenErrorInfo.ORDER_MODIFY_UNKNOW);
		}
	}

	private void verifyOrderStatus(DzopenOrderRel orderRel) {
		if (orderRel == null) {
			throw new DzopenException(DzopenErrorInfo.ORDER_MODIFY_ORDER_NOT_FOUND);
		}
		if (StringUtils.isEmpty(orderRel.getOrderNum())) {
			throw new DzopenException(DzopenErrorInfo.ORDER_MODIFY_ORDER_NOT_FOUND);
		}
		if (DzopenConsts.OrderConsumeStatus.normal != orderRel.getOrderConsumeStatus()) {
			throw new DzopenException(DzopenErrorInfo.ORDER_MODIFY_CONSUMED);
		}
	}

}
